package ga;

import NLP.Word2Vec;
import entity.Chromosome;
import entity.Word;
import util.JsonUtil4Word;

import java.util.*;

/**
 * @author: ChenforCode
 * @date: 2018/12/29 19:15
 * @description: 完成遗传算法
 */
public class GA {

    //种群
    private List<Chromosome> population;
    //所有的字词
    private List<Word> allWords;
    //定义五言绝句的四种平仄格式，平0 仄1  本次就采用第一种level1
    private static final String level1 = "x1001 00110 x0011 x1100";
    private static final String level2 = "00011 11100 11001 00110";
    private static final String level3 = "11100 00110 00011 11100";
    private static final String level4 = "00110 11100 11001 00110";

    //本次生成采用的韵律
    private String level;
    //主题词
    private Word themeWord;
    //本次采用的韵母
    private String yunmu;
    //采用24韵或者124韵  本次实验就采用24韵
    private String yunKind;

    private Word2Vec word2Vec = new Word2Vec();


    public void init(Word themeWord) {
        //设置全部的words
        JsonUtil4Word util = new JsonUtil4Word();
        this.allWords = util.parse("E:\\LiuChen\\IDEA Java\\GAPoemGenerator\\corpus\\simplified\\jueju\\words\\allWords.json");
        //设置主题词
        this.themeWord = themeWord;
        //选定韵律
        /*Random rand = new Random();
        int randResult = rand.nextInt(4);
        if (randResult == 0) {
            this.level = level1;
        } else if (randResult == 1) {
            this.level = level2;
        } else if (randResult == 2) {
            this.level = level3;
        } else {
            this.level = level4;
        }*/
        level = level1;
        //选定押韵格式
        /*int randResult_yun = rand.nextInt(2);
        if (randResult_yun == 0){
            yunKind = "24";
        } else if (randResult_yun == 1){
            yunKind = "124";
        }*/
        yunKind = "24";

        //TODO 选定韵母
        //选出来频率比较高的韵
        /*Iterator it = allWords.iterator();
        while (it.hasNext()){
            Word tempWord = (Word) it.next();
            List<String> tempYun = tempWord.getYunmu();
            for (int i = 0; i < tempYun.size(); i++){
                yunmuSet.add(tempYun.get(i));
            }
        }
        Map<String, Integer> map = new HashMap();
        Iterator setIt = yunmuSet.iterator();
        while (setIt.hasNext()){
            map.put((String) setIt.next(), 0);
        }

        Iterator it2 = allWords.iterator();
        while (it2.hasNext()){
            Word word = (Word) it2.next();
            List<String> yum = word.getYunmu();
            if (yum.size() == 1){
                map.put(yum.get(0), map.get(yum.get(0)) + 1);
            }
        }
        System.out.println(map);*/
/*        Iterator it2 = allWords.iterator();
        while (it.hasNext()){

        }*/
        //随机选韵
        Set<String> yunmuSet = new HashSet<String>();
        yunmuSet.add("ian");
        yunmuSet.add("ang");
        yunmuSet.add("eng");
        yunmuSet.add("ing");
        yunmuSet.add("uan");
        yunmuSet.add("ei");
        yunmuSet.add("e");
        yunmuSet.add("ou");
        yunmuSet.add("i");
        yunmuSet.add("ong");
        yunmuSet.add("an");
        yunmuSet.add("ao");
        yunmuSet.add("u");
        Object[] obj = yunmuSet.toArray();
        yunmu = (String) obj[(int) (Math.random() * obj.length)];
    }

    /**
     * @return void
     * @Author ChenforCode
     * @Description //TODO 初始种群生成  第一代生成1000首
     * @Date 19:16 2018/12/29
     * @Param []
     **/
    public void initPopulation() {

        System.out.println();
        word2Vec.loadModel();
        //初始化种群
        population = new ArrayList<Chromosome>();
        Random random = new Random();
        //遍历所有词语的迭代器
        final Iterator it = allWords.iterator();

        //获得具有本次韵母的所有的1一个长度的字
        List<Word> wordsWithYunmuOne = new ArrayList<Word>();
        while (it.hasNext()) {
            Word tempWord = (Word) it.next();
            if (tempWord.length() == 1) {
                String yun = tempWord.getYunmu().get(0);
                String tone = tempWord.getTone().get(0);
                if (yun.equals(yunmu) && (tone.equals("1") || tone.equals("2"))) {
                    wordsWithYunmuOne.add(tempWord);
                }
            }
        }


        //获得韵律符合x1的二字词语
        Iterator it2 = allWords.iterator();
        List<Word> _x1TwoWords = new ArrayList<Word>();
        while (it2.hasNext()) {
            Word tempWord = (Word) it2.next();
            if (tempWord.length() == 2) {
                if (tempWord.getTone().get(1).equals("3") || tempWord.getTone().get(1).equals("4")) {
                    _x1TwoWords.add(tempWord);
                }
            }
        }

        //按与主题词的相关度进行从大到小排序，取前1000位
        Collections.sort(_x1TwoWords, new Comparator<Word>() {
            public int compare(Word o1, Word o2) {
                //int diff = AssociationDegree.getAssociationDegree(o1, themeWord) - AssociationDegree.getAssociationDegree(o2, themeWord);
                float diff = word2Vec.similarity(o1.getWord(), themeWord.getWord()) - word2Vec.similarity(o2.getWord(), themeWord.getWord());
                if (diff < 0) {
                    return 1;
                } else if (diff > 0) {
                    return -1;
                }
                return 0;
            }
        });
        _x1TwoWords.subList(0, 1000);


        //获得韵律符合00的二字词语
        List<Word> _00TwoWords = new ArrayList<Word>();
        Iterator it3 = allWords.iterator();
        while (it3.hasNext()) {
            Word tempWord = (Word) it3.next();
            if (tempWord.length() == 2) {
                String tone1 = tempWord.getTone().get(0);
                String tone2 = tempWord.getTone().get(1);
                if ((tone1.equals("1") || tone1.equals("2")) && (tone2.equals("1") || tone2.equals("2"))) {
                    _00TwoWords.add(tempWord);
                }
            }
        }

        //按与主题词的相关度进行从大到小排序，取前1000位
        Collections.sort(_00TwoWords, new Comparator<Word>() {
            public int compare(Word o1, Word o2) {
                float diff = word2Vec.similarity(o1.getWord(), themeWord.getWord()) - word2Vec.similarity(o2.getWord(), themeWord.getWord());
                if (diff < 0) {
                    return 1;
                } else if (diff > 0) {
                    return -1;
                }
                return 0;
            }
        });
        _00TwoWords.subList(0, 1000);

        //获得韵律符合1的所有一字词语
        List<Word> _1OneWord = new ArrayList<Word>();
        Iterator it4 = allWords.iterator();
        while (it4.hasNext()) {
            Word tempWord = (Word) it4.next();
            if (tempWord.length() == 1) {
                if (tempWord.getTone().get(0).equals("3") || tempWord.getTone().get(0).equals("4")) {
                    _1OneWord.add(tempWord);
                }
            }
        }

        //按与主题词的相关度进行从大到小排序，取前1000位
        Collections.sort(_1OneWord, new Comparator<Word>() {
            public int compare(Word o1, Word o2) {
                float diff = word2Vec.similarity(o1.getWord(), themeWord.getWord()) - word2Vec.similarity(o2.getWord(), themeWord.getWord());
                if (diff < 0) {
                    return 1;
                } else if (diff > 0) {
                    return -1;
                }
                return 0;
            }
        });
        _1OneWord.subList(0, 1000);

        //获得韵律符合10的所有二字词语
        List<Word> _10TwoWords = new ArrayList<Word>();
        Iterator it5 = allWords.iterator();
        while (it5.hasNext()) {
            Word tempWord = (Word) it5.next();
            if (tempWord.length() == 2) {
                String tone1 = tempWord.getTone().get(0);
                String tone2 = tempWord.getTone().get(1);
                if ((tone1.equals("3") || tone1.equals("4")) && (tone2.equals("1") || tone2.equals("2"))) {
                    _10TwoWords.add(tempWord);
                }
            }
        }

        //按与主题词的相关度进行从大到小排序，取前1000位
        Collections.sort(_10TwoWords, new Comparator<Word>() {
            public int compare(Word o1, Word o2) {
                float diff = word2Vec.similarity(o1.getWord(), themeWord.getWord()) - word2Vec.similarity(o2.getWord(), themeWord.getWord());
                if (diff < 0) {
                    return 1;
                } else if (diff > 0) {
                    return -1;
                }
                return 0;
            }
        });
        _10TwoWords.subList(0, 1000);

        //获得韵律符合x0的所有二字词语
        List<Word> _x0TwoWords = new ArrayList<Word>();
        Iterator it6 = allWords.iterator();
        while (it6.hasNext()) {
            Word tempWord = (Word) it6.next();
            if (tempWord.length() == 2) {
                if (tempWord.getTone().get(1).equals("1") || tempWord.getTone().get(1).equals("2")) {
                    _x0TwoWords.add(tempWord);
                }
            }
        }

        //按与主题词的相关度进行从大到小排序，取前1000位
        Collections.sort(_x0TwoWords, new Comparator<Word>() {
            public int compare(Word o1, Word o2) {
                float diff = word2Vec.similarity(o1.getWord(), themeWord.getWord()) - word2Vec.similarity(o2.getWord(), themeWord.getWord());
                if (diff < 0) {
                    return 1;
                } else if (diff > 0) {
                    return -1;
                }
                return 0;
            }
        });
        _x0TwoWords.subList(0, 1000);

        //获得韵律符合01的所有二字词语
        List<Word> _01TwoWords = new ArrayList<Word>();
        Iterator it7 = allWords.iterator();
        while (it7.hasNext()) {
            Word tempWord = (Word) it7.next();
            if (tempWord.length() == 2) {
                String tone1 = tempWord.getTone().get(0);
                String tone2 = tempWord.getTone().get(1);
                if ((tone1.equals("1") || tone1.equals("2")) && (tone2.equals("3") || tone2.equals("4"))) {
                    _01TwoWords.add(tempWord);
                }
            }
        }

        //按与主题词的相关度进行从大到小排序，取前1000位
        Collections.sort(_01TwoWords, new Comparator<Word>() {
            public int compare(Word o1, Word o2) {
                float diff = word2Vec.similarity(o1.getWord(), themeWord.getWord()) - word2Vec.similarity(o2.getWord(), themeWord.getWord());
                if (diff < 0) {
                    return 1;
                } else if (diff > 0) {
                    return -1;
                }
                return 0;
            }
        });
        _01TwoWords.subList(0, 1000);

        //获得韵律符合0的所有一字词语
        List<Word> _0OneWord = new ArrayList<Word>();
        Iterator it8 = allWords.iterator();
        while (it8.hasNext()) {
            Word tempWord = (Word) it8.next();
            if (tempWord.length() == 1) {
                if (tempWord.getTone().get(0).equals("1") || tempWord.getTone().get(0).equals("2")) {
                    _0OneWord.add(tempWord);
                }
            }
        }

        //按与主题词的相关度进行从大到小排序，取前1000位
        Collections.sort(_0OneWord, new Comparator<Word>() {
            public int compare(Word o1, Word o2) {
                float diff = word2Vec.similarity(o1.getWord(), themeWord.getWord()) - word2Vec.similarity(o2.getWord(), themeWord.getWord());
                if (diff < 0) {
                    return 1;
                } else if (diff > 0) {
                    return -1;
                }
                return 0;
            }
        });
        _0OneWord.subList(0, 1000);

        //获得韵律符合11的所有二字词语
        List<Word> _11TwoWords = new ArrayList<Word>();
        Iterator it9 = allWords.iterator();
        while (it9.hasNext()) {
            Word tempWord = (Word) it9.next();
            if (tempWord.length() == 2) {
                String tone1 = tempWord.getTone().get(0);
                String tone2 = tempWord.getTone().get(1);
                if ((tone1.equals("3") || tone1.equals("4")) && (tone2.equals("3") || tone2.equals("4"))) {
                    _11TwoWords.add(tempWord);
                }
            }
        }

        //按与主题词的相关度进行从大到小排序，取前1000位
        Collections.sort(_11TwoWords, new Comparator<Word>() {
            public int compare(Word o1, Word o2) {
                float diff = word2Vec.similarity(o1.getWord(), themeWord.getWord()) - word2Vec.similarity(o2.getWord(), themeWord.getWord());
                if (diff < 0) {
                    return 1;
                } else if (diff > 0) {
                    return -1;
                }
                return 0;
            }
        });
        _11TwoWords.subList(0, 1000);

        //白日 依山 尽 , 黄河 入海 流 。 欲穷 千里 目 , 更上 一层 楼 。
        //"x1001           00110          x0011          x1100"
        //结尾1二字  两个0二字 11一二字  0韵  结尾0二字 01二字 0一字
        /*for (int i = 0; i < 1000; i++) {

        }*/

        //开始生成初始种群，里边包含100条染色体
        for (int i = 0; i < 100; i++) {
            Chromosome chromosome = new Chromosome();
            List<Word> gene = new ArrayList<Word>();
            //共需要12个word
            Word w1 = _x1TwoWords.get(random.nextInt(_x1TwoWords.size()));
            Word w2 = _00TwoWords.get(random.nextInt(_00TwoWords.size()));
            Word w3 = _1OneWord.get(random.nextInt(_1OneWord.size()));
            Word w4 = _00TwoWords.get(random.nextInt(_00TwoWords.size()));
            Word w5 = _11TwoWords.get(random.nextInt(_11TwoWords.size()));
            Word w6 = wordsWithYunmuOne.get(random.nextInt(wordsWithYunmuOne.size()));
            Word w7 = _10TwoWords.get(random.nextInt(_10TwoWords.size()));
            Word w8 = _01TwoWords.get(random.nextInt(_01TwoWords.size()));
            Word w9 = _1OneWord.get(random.nextInt(_1OneWord.size()));
            Word w10 = _x1TwoWords.get(random.nextInt(_x1TwoWords.size()));
            Word w11 = _10TwoWords.get(random.nextInt(_10TwoWords.size()));
            Word w12 = wordsWithYunmuOne.get(random.nextInt(wordsWithYunmuOne.size()));
            gene.add(w1);
            gene.add(w2);
            gene.add(w3);
            gene.add(w4);
            gene.add(w5);
            gene.add(w6);
            gene.add(w7);
            gene.add(w8);
            gene.add(w9);
            gene.add(w10);
            gene.add(w11);
            gene.add(w12);
            chromosome.setGene(gene);
            chromosome.calcFitness(themeWord);
            population.add(chromosome);
        }
    }

    /**
     * @return void
     * @Author ChenforCode
     * @Description //TODO 选择个体进入下一代进行交叉操作
     * @Date 19:21 2018/12/29
     * @Param []
     **/
    public void select() {
        double sumFitness = 0;
        //得到所有个体的适应度之和
        Iterator<Chromosome> it = population.iterator();
        while (it.hasNext()) {
            Chromosome tempChrom = it.next();
            sumFitness += tempChrom.getFitness();
        }

        //进行排序
        Collections.sort(population, new Comparator<Chromosome>() {
            public int compare(Chromosome o1, Chromosome o2) {
                if (o1.getFitness() < o2.getFitness()) {
                    return 1;
                } else if (o1.getFitness() > o2.getFitness()) {
                    return 0;
                }
                return 0;
            }
        });


        List<Chromosome> old = population;
        Iterator<Chromosome> it1 = old.iterator();
        //创建新一代的留下来的个体
        population = new ArrayList<Chromosome>();
        int index = 0;
        //加入适应度最高的一个个体，即精英主义
        population.add(it1.next());
        while (it1.hasNext()) {
            Chromosome tempChrom = it1.next();
            double poss = tempChrom.getFitness() / sumFitness;
            double result = Math.random();
            //如果轮盘赌赌中了就留下来
            if (result >= 0 && result <= poss) {
                population.add(tempChrom);
            }
        }
    }

    /**
     * @return void
     * @Author ChenforCode
     * @Description //TODO 交叉操作
     * @Date 19:20 2018/12/29
     * @Param []
     **/
    public void cross() {
        //对除了精英之外的所有个体进行交叉
        for (int i = 1; i < population.size() - 1; i++) {
            Random random = new Random();
            //产生0-11的随机位置交叉
            int pos = random.nextInt(12);
            //得到两个需要交叉的染色体
            Chromosome c1 = population.get(i);
            Chromosome c2 = population.get(i + 1);
            //得到基因
            List<Word> gene1 = c1.getGene();
            List<Word> gene2 = c2.getGene();
            //得到要交换的词
            Word word1 = gene1.get(pos);
            Word word2 = gene2.get(pos);
            //更换基因
            gene1.set(pos, word2);
            gene2.set(pos, word1);
            //设置染色体的基因
            c1.setGene(gene1);
            c2.setGene(gene2);
            //更换种群中的染色体
            population.set(i, c1);
            population.set(i + 1, c2);
        }
    }

    /**
     * @return void
     * @Author ChenforCode
     * @Description //TODO 执行变异操作
     * @Date 19:27 2018/12/29
     * @Param []
     **/
    public void variation() {
        word2Vec.loadModel();
        int variaCount = 3;
        for (int i = 0; i < variaCount; i++) {
            Random random = new Random();
            //选择变异哪个个体
            int pos = random.nextInt(population.size());
            Chromosome chrom = population.get(pos);
            List<Word> gene = chrom.getGene();
            int posGene = random.nextInt(12);
            //押韵位变异
            if (posGene == 5 || posGene == 11) {
                Word word = gene.get(posGene);
                Collections.sort(allWords, new Comparator<Word>() {
                    public int compare(Word o1, Word o2) {
                        float diff = word2Vec.similarity(o1.getWord(), themeWord.getWord()) - word2Vec.similarity(o2.getWord(), themeWord.getWord());
                        if (diff < 0) {
                            return 1;
                        } else if (diff > 0) {
                            return -1;
                        }
                        return 0;
                    }
                });

                for (int j = 0; j < allWords.size(); j++) {
                    Word testWord = allWords.get(j);
                    //相关度优先，并且保证韵律，押韵和词性都相同
                    if (testWord.getYunmu() == word.getYunmu() && testWord.getTone() == word.getTone() && testWord.getPos() == word.getPos()) {
                        gene.set(posGene, testWord);
                    }
                }
            } else {
                Word word = gene.get(posGene);
                Collections.sort(allWords, new Comparator<Word>() {
                    public int compare(Word o1, Word o2) {
                        float diff = word2Vec.similarity(o1.getWord(), themeWord.getWord()) - word2Vec.similarity(o2.getWord(), themeWord.getWord());
                        if (diff < 0) {
                            return 1;
                        } else if (diff > 0) {
                            return -1;
                        }
                        return 0;
                    }
                });

                for (int j = 0; j < allWords.size(); j++) {
                    Word testWord = allWords.get(j);
                    //相关度优先，并且保证韵律，押韵和词性都相同
                    if (testWord.getTone() == word.getTone() && testWord.getPos() == word.getPos()) {
                        gene.set(posGene, testWord);
                    }
                }
            }
            //基因变异之后设置新的基因
            chrom.setGene(gene);
            //设置新的染色体
            population.set(pos, chrom);
        }
    }

    public List<Chromosome> getPopulation() {
        return population;
    }

    public static void main(String[] args) {
        GA ga = new GA();
        Word word = new Word();
        word.setWord("春");
        //进化一百代
        for (int i = 0; i < 3; i++){
            ga.init(word);
            ga.initPopulation();
            ga.select();
            System.out.println("aaaa");
            ga.cross();
            System.out.println("bbb");
            ga.variation();
        }
        Iterator it = ga.getPopulation().iterator();
        while (it.hasNext()){
            Chromosome chrom = (Chromosome) it.next();
            List<Word> gene = chrom.getGene();
            String poem = "";
            for (int i = 0; i <= gene.size(); i++){
                poem += gene.get(i);
                if (poem.length() == 5 || poem.length() == 10 || poem.length() == 15){
                    poem += ",";
                }
            }
            poem += "。";
            System.out.println(poem);
        }
    }
}
